package pl.edu.agh.jemo.gui;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import javax.swing.DefaultComboBoxModel;
import javax.swing.DefaultListModel;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSeparator;
import javax.swing.JTabbedPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;

import net.miginfocom.swing.MigLayout;

import org.apache.log4j.Level;

import pl.edu.agh.jemo.conf.types.AlgorithmTypesHandler;
import pl.edu.agh.jemo.conf.types.CrossoverTypesHandler;
import pl.edu.agh.jemo.conf.types.GenotypeTypesHandler;
import pl.edu.agh.jemo.conf.types.MutationTypesHandler;
import pl.edu.agh.jemo.conf.types.ObjectiveFunctionTypesHandler;
import pl.edu.agh.jemo.conf.types.PopulationTypesHandler;
import pl.edu.agh.jemo.conf.types.PrintableType;
import pl.edu.agh.jemo.conf.types.SelectionTypesHandler;
import pl.edu.agh.jemo.conf.types.SpecimenTypesHandler;
import pl.edu.agh.jemo.evolution.algorithm.AbstractAlgorithm;
import pl.edu.agh.jemo.evolution.genotype.Genotype;
import pl.edu.agh.jemo.evolution.objfunc.ClassicObjectiveFunction;
import pl.edu.agh.jemo.evolution.objfunc.CrowdingObjectiveFunction;
import pl.edu.agh.jemo.evolution.objfunc.ObjectiveFunctionSet;
import pl.edu.agh.jemo.evolution.operator.crossover.Crossover;
import pl.edu.agh.jemo.evolution.operator.mutation.Mutation;
import pl.edu.agh.jemo.evolution.population.Population;
import pl.edu.agh.jemo.evolution.selections.Tournament;
import pl.edu.agh.jemo.evolution.specimen.Specimen;

/**
 * Main graphical user interface class. Responsible for showing main window, which is central place of jemo.
 * It allows user to configure every part of algorithm, and browse run results. It uses configuration panels, attached to every type, for detailed configuration. 
 * 
 * @author Marcin Zbijowski
 *
 */
public class MainWindow extends JFrame {
	/**
	 * As JFrame is implementation of Serializable interface, it requires this field to be set.
	 */
	private static final long serialVersionUID = 1L;

	/**
	 * Objective function set initialized by window, and passed to algorithm on run.
	 */
	private ObjectiveFunctionSet objectiveFunctionSet = new ObjectiveFunctionSet();
	/**
	 * Algorithm reference, created inside window.
	 */
	private AbstractAlgorithm algorithm = null;
	/**
	 * Type of genotype used by specimen in algorithm
	 */
	private Class<? extends Genotype> genotypeType = null;
	/**
	 * Mutatuion initialized by window and used by algorithm.
	 */
	private Mutation mutation = null;
	/**
	 * Crossover initialized by window and used by algorithm.
	 */
	private Crossover crossover = null;
	/**
	 * Population initialized by window and used by algorithm.
	 */
	private Population population = null;
	/**
	 * Tournament initialized by window and used by algorithm.
	 */
	private Tournament tournament = null;
	/**
	 * Type of specimen used by algorithm
	 */
	private Class<? extends Specimen> specimenType = null;
	
	/**
	 * Combo box containing all available classic objective functions.
	 * Filled using types handler class.
	 */
	private JComboBox classicObjFunCombo;
	/**
	 * Combo box containing all available crowding objective functions.
	 * Filled using types handler class.
	 */
	private JComboBox crowdingObjFunCombo;
	/**
	 * List of all objective functions chosen by user.
	 */
	private JList objectiveFunctionsList;
	/**
	 * Panel containing controls to choose objective functions.
	 */
	private JPanel configurationPanel;
	/**
	 * Combo box containing all available algorithms.
	 * Filled using types handler class.
	 */
	private JComboBox algorithmCombo;
	/**
	 * Text Field containing number of specimen in population.
	 */
	private JTextField populationSizeText;
	/**
	 * Text Field allowing to set number of steps to be made by algorithm.
	 */
	private JTextField stepsText;
	/**
	 * Panel containing controls used for algorithm configuration.
	 */
	private JPanel algorithmConfigurationPanel;
	/**
	 * Combo box containing all available specimen types.
	 * Filled using types handler class.
	 */
	private JComboBox specimenTypeCombo;
	/**
	 * Combo box containing all available mutations.
	 * Filled using types handler class.
	 */
	private JComboBox mutationCombo;
	/**
	 * Combo box containing all available crossovers.
	 * Filled using types handler class.
	 */
	private JComboBox crossoverCombo;
	/**
	 * Combo box containing all available tournaments.
	 * Filled using types handler class.
	 */
	private JComboBox tournamentCombo;
	/**
	 * Panel used to configure mutation. It's content depends on type of configured mutation.
	 */
	private JPanel mutationConfPanel;
	/**
	 * Panel used to configure crossover. It's content depends on type of configured crossover.
	 */
	private JPanel crossoverConfPanel;
	/**
	 * Panel used to configure tournament. It's content depends on type of configured tournament.
	 */
	private JPanel tournamentConfPanel;
	/**
	 * Combo box containing all available genotype types.
	 * Filled using types handler class.
	 */
	private JComboBox genotypeTypeCombo;
	/**
	 * Combo box containing all available population types.
	 * Filled using types handler class.
	 */
	private JComboBox populationTypeCombo;
	/**
	 * Text area on which logs are printed.
	 */
	private JTextArea logArea;
	/**
	 * Combo box with all currently performed algorithm runs.
	 */
	private JComboBox runCombo;
	/**
	 * Combo box containing all plot names for chosen run.
	 */
	private JComboBox resultsCombo;
	/**
	 * Text area on which short algorithm run summary is printed.
	 */
	private JTextArea overviewText;
	/**
	 * Allows to choose step number, for which results should be printed.
	 */
	private JComboBox stepsCombo;
	/**
	 * List of result population's genotypes in chosen step.
	 */
	private JList genotypeList;
	/**
	 * List of result population's phenotypes in chosen step.
	 */
	private JList phenotypeList;
	/**
	 * String path to population genotype plot for currently chosen step.
	 */
	private String genotypeImage = null;
	/**
	 * String path to population phenotype plot for currently chosen step.
	 */
	private String phenotypeImage = null;
	/**
	 * Combo box with list of used in jemo log levels. Priorities of levels can be found in log4j documentation.
	 */
	private JComboBox logLevelComboBox;
	
	/**
	 * Creates main application window.
	 * Initializes all components in that window. Fills lists of configurable types using types handlers.
	 * If any chosen operation requires additional setup, desplays associated with it configuration panel.
	 */
	@SuppressWarnings("unchecked")
	public MainWindow() {
		super();
		try {
			UIManager.setLookAndFeel("com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel");
		} catch (UnsupportedLookAndFeelException e) {
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		}
		setTitle("jEMO");
		setDefaultCloseOperation(EXIT_ON_CLOSE);
		setIconImage(Toolkit.getDefaultToolkit().getImage("resources\\emo.gif"));

		setMinimumSize(new Dimension(598, 496));
		setPreferredSize(getSize());
		getContentPane().setLayout(new MigLayout("", "[grow]", "[grow]"));
		
		JPanel mainPanel = new JPanel();
		getContentPane().add(mainPanel, "cell 0 0,grow");
		mainPanel.setLayout(new MigLayout("", "[grow]", "[grow]"));
		
		JTabbedPane tabbedPane = new JTabbedPane(JTabbedPane.LEFT);
		mainPanel.add(tabbedPane, "flowx,cell 0 0,grow");
		tabbedPane.addChangeListener(new ChangeListener() {
			@Override
			public void stateChanged(ChangeEvent event) {
				refreshRunsList();
			}
		});
		
		JPanel problemPanel = new JPanel();
		tabbedPane.addTab("Problem", null, problemPanel, null);
		problemPanel.setLayout(null);
		
		JLabel classicObjFunLabel = new JLabel("Classic objective functions:");
		classicObjFunLabel.setBounds(10, 11, 185, 14);
		problemPanel.add(classicObjFunLabel);
		
		classicObjFunCombo = new JComboBox(ObjectiveFunctionTypesHandler.getClassicObjectiveFunctionTypes().toArray());
		classicObjFunCombo.setBounds(10, 29, 185, 22);
		problemPanel.add(classicObjFunCombo);
		
		JButton classicObjFunAddBtn = new JButton("Add");
		classicObjFunAddBtn.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent event) {
				try {
					objectiveFunctionSet.add(((PrintableType<? extends ClassicObjectiveFunction>)classicObjFunCombo.getSelectedItem()).getClassType().newInstance());
					refreshObjFunList();
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		});
		classicObjFunAddBtn.setBounds(205, 29, 60, 23);
		problemPanel.add(classicObjFunAddBtn);
		
		JLabel crowdingObjFunLabel = new JLabel("Crowding objective functions:");
		crowdingObjFunLabel.setBounds(10, 58, 185, 14);
		problemPanel.add(crowdingObjFunLabel);
		
		crowdingObjFunCombo = new JComboBox(ObjectiveFunctionTypesHandler.getCrowdingObjectiveFunctionTypes().toArray());
		crowdingObjFunCombo.setBounds(10, 76, 185, 22);
		problemPanel.add(crowdingObjFunCombo);
		
		JButton crowdingObjFunAddBtn = new JButton("Add");
		crowdingObjFunAddBtn.setBounds(205, 76, 60, 23);
		problemPanel.add(crowdingObjFunAddBtn);
		crowdingObjFunAddBtn.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent event) {
				try {
					objectiveFunctionSet.add(((PrintableType<? extends CrowdingObjectiveFunction>)crowdingObjFunCombo.getSelectedItem()).getClassType().newInstance());
					refreshObjFunList();
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		});
		
		JScrollPane scrollPane = new JScrollPane();
		scrollPane.setBounds(10, 109, 255, 257);
		problemPanel.add(scrollPane);
		
		objectiveFunctionsList = new JList();
		scrollPane.setViewportView(objectiveFunctionsList);
		objectiveFunctionsList.addListSelectionListener(new ListSelectionListener() {	
			@Override
			public void valueChanged(ListSelectionEvent event) {
				configurationPanel.removeAll();
				if (objectiveFunctionsList.getSelectedIndex() >= 0) {
					if (objectiveFunctionSet.get(objectiveFunctionsList.getSelectedIndex()).getConfigPanel() != null) {
						configurationPanel.add(objectiveFunctionSet.get(objectiveFunctionsList.getSelectedIndex()).getConfigPanel(), BorderLayout.CENTER);
					}
				}
				configurationPanel.revalidate();
				configurationPanel.repaint();
			}
		});
		
		JButton objectiveFunctionRemoveBtn = new JButton("Remove");
		objectiveFunctionRemoveBtn.setBounds(176, 370, 89, 23);
		problemPanel.add(objectiveFunctionRemoveBtn);
		objectiveFunctionRemoveBtn.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent event) {
				objectiveFunctionSet.remove(objectiveFunctionsList.getSelectedIndex());
				refreshObjFunList();
			}
		});
		
		configurationPanel = new JPanel();
		configurationPanel.setBounds(274, 11, 221, 382);
		problemPanel.add(configurationPanel);
		configurationPanel.setLayout(new BorderLayout(0, 0));
		tabbedPane.setEnabledAt(0, true);
		
		JPanel algorithmPanel = new JPanel();
		tabbedPane.addTab("Algorithm", null, algorithmPanel, null);
		algorithmPanel.setLayout(null);
		
		JLabel algorithmLabel = new JLabel("Algorithm:");
		algorithmLabel.setBounds(10, 11, 129, 14);
		algorithmPanel.add(algorithmLabel);
		
		algorithmCombo = new JComboBox(AlgorithmTypesHandler.getAlgorithmTypes().toArray());
		algorithmCombo.setBounds(10, 26, 163, 22);
		algorithmPanel.add(algorithmCombo);
		try {
			algorithm = ((PrintableType<? extends AbstractAlgorithm>)algorithmCombo.getModel().getSelectedItem()).getClassType().newInstance();
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		algorithmCombo.addActionListener(new ActionListener() {	
			@Override
			public void actionPerformed(ActionEvent event) {
				try {
					algorithm = ((PrintableType<? extends AbstractAlgorithm>)algorithmCombo.getModel().getSelectedItem()).getClassType().newInstance();
				} catch (Exception e) {
					e.printStackTrace();
				}
				algorithmConfigurationPanel.removeAll();
				if (algorithm.getConfigPanel() != null) {
					algorithmConfigurationPanel.add(algorithm.getConfigPanel(), BorderLayout.CENTER);
				}
				algorithmConfigurationPanel.revalidate();
				algorithmConfigurationPanel.repaint();
				setSpecimenCombo();
			}
		});
		
		JLabel populationSizeLabel = new JLabel("Number of specimen:");
		populationSizeLabel.setBounds(10, 59, 142, 14);
		algorithmPanel.add(populationSizeLabel);
		
		populationSizeText = new JTextField();
		if (algorithm != null) {
			populationSizeText.setText(String.valueOf(algorithm.getPopulationSize()));
		} else {
			populationSizeText.setText("");
		}
		populationSizeText.setBounds(10, 75, 163, 22);
		algorithmPanel.add(populationSizeText);
		populationSizeText.setColumns(10);
		
		JLabel stepsLabel = new JLabel("Number of generations:");
		stepsLabel.setBounds(10, 106, 142, 14);
		algorithmPanel.add(stepsLabel);
		
		stepsText = new JTextField();
		if (algorithm != null) {
			stepsText.setText(String.valueOf(algorithm.getSteps()));
		} else {
			stepsText.setText("");
		}
		stepsText.setBounds(10, 125, 163, 22);
		algorithmPanel.add(stepsText);
		stepsText.setColumns(10);
		
		JLabel populationTypeLabel = new JLabel("Population type:");
		populationTypeLabel.setBounds(10, 156, 129, 14);
		algorithmPanel.add(populationTypeLabel);
		
		populationTypeCombo = new JComboBox(PopulationTypesHandler.getPopulationTypes().toArray());
		populationTypeCombo.setBounds(10, 170, 163, 22);
		algorithmPanel.add(populationTypeCombo);
		populationTypeCombo.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent event) {
				try {
					population = ((PrintableType<? extends Population>)populationTypeCombo.getModel().getSelectedItem()).getClassType().newInstance();
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		});
		try {
			population = ((PrintableType<? extends Population>)populationTypeCombo.getModel().getSelectedItem()).getClassType().newInstance();
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		JLabel specimenTypeLabel = new JLabel("Specimen type:");
		specimenTypeLabel.setBounds(10, 203, 129, 14);
		algorithmPanel.add(specimenTypeLabel);
		
		specimenTypeCombo = new JComboBox();
		specimenTypeCombo.setBounds(10, 217, 163, 22);
		algorithmPanel.add(specimenTypeCombo);
		setSpecimenCombo();
		specimenTypeCombo.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent event) {
				specimenType = ((PrintableType<? extends Specimen>)specimenTypeCombo.getSelectedItem()).getClassType();
			}
		});
		specimenType = ((PrintableType<? extends Specimen>)specimenTypeCombo.getSelectedItem()).getClassType();

		
		JLabel genotypeTypeLabel = new JLabel("Genotype type:");
		genotypeTypeLabel.setBounds(10, 250, 129, 14);
		algorithmPanel.add(genotypeTypeLabel);
		
		genotypeTypeCombo = new JComboBox(GenotypeTypesHandler.getGenotypeTypes().toArray());
		genotypeTypeCombo.setBounds(10, 267, 163, 22);
		algorithmPanel.add(genotypeTypeCombo);
		genotypeTypeCombo.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent event) {
				genotypeType = ((PrintableType<? extends Genotype>)genotypeTypeCombo.getSelectedItem()).getClassType();
			}
		});
		genotypeType = ((PrintableType<? extends Genotype>)genotypeTypeCombo.getSelectedItem()).getClassType();
		
		algorithmConfigurationPanel = new JPanel();
		algorithmConfigurationPanel.setBounds(236, 11, 254, 387);
		algorithmPanel.add(algorithmConfigurationPanel);
		algorithmConfigurationPanel.setLayout(new BorderLayout(0, 0));
		
		JPanel operatorsPanel = new JPanel();
		tabbedPane.addTab("Operators", null, operatorsPanel, null);
		operatorsPanel.setLayout(null);
		
		JSeparator upperSeparator = new JSeparator();
		upperSeparator.setBounds(10, 136, 476, 2);
		operatorsPanel.add(upperSeparator);
		
		JSeparator lowerSeparator = new JSeparator();
		lowerSeparator.setBounds(10, 288, 476, 2);
		operatorsPanel.add(lowerSeparator);
		
		JLabel mutationLabel = new JLabel("Mutation:");
		mutationLabel.setBounds(10, 11, 102, 14);
		operatorsPanel.add(mutationLabel);
		
		JLabel crossoverLabel = new JLabel("Crossover:");
		crossoverLabel.setBounds(10, 143, 121, 14);
		operatorsPanel.add(crossoverLabel);
		
		JLabel tournamentLabel = new JLabel("Tournament:");
		tournamentLabel.setBounds(10, 301, 121, 14);
		operatorsPanel.add(tournamentLabel);

		mutationConfPanel = new JPanel();
		mutationConfPanel.setBounds(190, 11, 296, 116);
		operatorsPanel.add(mutationConfPanel);
		mutationConfPanel.setLayout(new BorderLayout(0, 0));
		
		crossoverConfPanel = new JPanel();
		crossoverConfPanel.setBounds(190, 149, 296, 128);
		operatorsPanel.add(crossoverConfPanel);
		crossoverConfPanel.setLayout(new BorderLayout(0, 0));
		
		tournamentConfPanel = new JPanel();
		tournamentConfPanel.setBounds(190, 301, 296, 137);
		operatorsPanel.add(tournamentConfPanel);
		tournamentConfPanel.setLayout(new BorderLayout(0, 0));

		mutationCombo = new JComboBox(MutationTypesHandler.getMutationTypes().toArray());
		mutationCombo.setBounds(10, 27, 170, 22);
		operatorsPanel.add(mutationCombo);
		mutationCombo.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent event) {
				setMutationPanel();
			}
		});
		setMutationPanel();
		
		crossoverCombo = new JComboBox(CrossoverTypesHandler.getCrossoverTypes().toArray());
		crossoverCombo.setBounds(10, 159, 170, 22);
		operatorsPanel.add(crossoverCombo);
		crossoverCombo.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent event) {
				setCrossoverPanel();
			}
		});
		setCrossoverPanel();
		
		tournamentCombo = new JComboBox(SelectionTypesHandler.getTournamentTypes().toArray());
		tournamentCombo.setBounds(10, 317, 170, 22);
		operatorsPanel.add(tournamentCombo);
		tournamentCombo.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent event) {
				setTournamentPanel();
			}
		});
		setTournamentPanel();

		JPanel runPanel = new JPanel();
		tabbedPane.addTab("Run", null, runPanel, null);
		runPanel.setLayout(null);
		
		JScrollPane logScrollPane = new JScrollPane();
		logScrollPane.setBounds(10, 11, 464, 362);
		runPanel.add(logScrollPane);
		
		logArea = new JTextArea();
		logScrollPane.setViewportView(logArea);
		LoggerHelper.getInstance().addJTextareaAppender(logArea);
		
		JButton runButton = new JButton("Start algorithm");
		runButton.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent event) {
				algorithm.setCrossover(crossover);
				algorithm.setGenotypeType(genotypeType);
				algorithm.setMutation(mutation);
				algorithm.setObjectiveFunctionSet(objectiveFunctionSet);
				algorithm.setPopulation(population);
				algorithm.setTournament(tournament);
				algorithm.setSpecimenType(specimenType);
				algorithm.start();
			}
		});
		runButton.setBounds(355, 384, 119, 23);
		runPanel.add(runButton);
		
		JLabel lblLogLevel = new JLabel("Log level:");
		lblLogLevel.setBounds(10, 384, 74, 14);
		runPanel.add(lblLogLevel);
		
		Level[] levels = {Level.INFO, Level.ERROR, Level.WARN, Level.DEBUG};
		logLevelComboBox = new JComboBox(levels);
		logLevelComboBox.setBounds(94, 384, 119, 20);
		runPanel.add(logLevelComboBox);
		logLevelComboBox.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				LoggerHelper.getInstance().setLevel((Level)logLevelComboBox.getSelectedItem());
			}
		});
		
		
		JPanel resultPanel = new JPanel();
		tabbedPane.addTab("Results", null, resultPanel, null);
		resultPanel.setLayout(null);
		
		JLabel runLabel = new JLabel("Run:");
		runLabel.setBounds(10, 11, 46, 14);
		resultPanel.add(runLabel);
		
		runCombo = new JComboBox();
		runCombo.setBounds(42, 7, 153, 22);
		resultPanel.add(runCombo);
		runCombo.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent arg0) {
				refreshRunResultList();
			}
		});
		refreshRunsList();
		
		JTabbedPane resultTabPane = new JTabbedPane(JTabbedPane.TOP);
		resultTabPane.setBounds(10, 36, 476, 382);
		resultPanel.add(resultTabPane);
		
		JPanel overviewPanel = new JPanel();
		resultTabPane.addTab("Overview", null, overviewPanel, null);
		overviewPanel.setLayout(new BorderLayout(0, 0));
		
		JScrollPane overviewScrollPane = new JScrollPane();
		overviewPanel.add(overviewScrollPane, BorderLayout.CENTER);
		
		overviewText = new JTextArea();
		overviewText.setEditable(false);
		overviewScrollPane.setViewportView(overviewText);
		
		JPanel chartPanel = new JPanel();
		resultTabPane.addTab("Charts", null, chartPanel, null);
		chartPanel.setLayout(null);
		
		JLabel genotypeImgLabel = new JLabel("Genotype:");
		genotypeImgLabel.setBounds(24, 148, 139, 14);
		chartPanel.add(genotypeImgLabel);
		
		JLabel phenotypeImgLabel = new JLabel("Phenotype:");
		phenotypeImgLabel.setBounds(302, 148, 139, 14);
		chartPanel.add(phenotypeImgLabel);
		
		JButton genotypeFullButton = new JButton("Full size");
		genotypeFullButton.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				if (genotypeImage == null) {
					return;
				}
				new ImageViewer(genotypeImage);
			}
		});
		genotypeFullButton.setBounds(24, 173, 91, 23);
		chartPanel.add(genotypeFullButton);
		
		JButton phenotypeFullBtn = new JButton("Full size");
		phenotypeFullBtn.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				if (phenotypeImage == null) {
					return;
				}
				new ImageViewer(phenotypeImage);
			}
		});
		phenotypeFullBtn.setBounds(302, 173, 91, 23);
		chartPanel.add(phenotypeFullBtn);
		
		JPanel dataPanel = new JPanel();
		resultTabPane.addTab("Data", null, dataPanel, null);
		dataPanel.setLayout(null);
		
		JLabel genotypeResultLabel = new JLabel("Genotype:");
		genotypeResultLabel.setBounds(5, 5, 111, 14);
		dataPanel.add(genotypeResultLabel);
		
		JLabel phenotypeResultLabel = new JLabel("Phenotype:");
		phenotypeResultLabel.setBounds(235, 5, 97, 14);
		dataPanel.add(phenotypeResultLabel);
		
		JScrollPane genotypeScrollPane = new JScrollPane();
		genotypeScrollPane.setBounds(0, 21, 220, 325);
		dataPanel.add(genotypeScrollPane);
		
		genotypeList = new JList();
		genotypeScrollPane.setViewportView(genotypeList);
		
		JScrollPane phenotypeScrollPane = new JScrollPane();
		phenotypeScrollPane.setBounds(230, 21, 220, 325);
		dataPanel.add(phenotypeScrollPane);
		
		phenotypeList = new JList();
		phenotypeScrollPane.setViewportView(phenotypeList);
		
		JLabel resultsLabel = new JLabel("Results:");
		resultsLabel.setBounds(205, 11, 46, 14);
		resultPanel.add(resultsLabel);
		
		resultsCombo = new JComboBox();
		resultsCombo.setBounds(249, 7, 129, 22);
		resultPanel.add(resultsCombo);
		resultsCombo.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent arg0) {
				refreshStepsList();
			}
		});
		
		JLabel stepLabel = new JLabel("Step:");
		stepLabel.setBounds(388, 11, 46, 14);
		resultPanel.add(stepLabel);
		
		stepsCombo = new JComboBox();
		stepsCombo.setBounds(424, 7, 51, 22);
		resultPanel.add(stepsCombo);
		stepsCombo.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent arg0) {
				printStepResults();
			}
		});
		
		setVisible(true);
		setLocationRelativeTo(null);
	}

	/**
	 * Prints currently chosen objective functions into JList with problem definition.
	 */
	private void refreshObjFunList() {
		DefaultListModel model = new DefaultListModel();
		for (int i = 0 ; i < objectiveFunctionSet.size() ; i++) {
			model.add(i, objectiveFunctionSet.get(i));
		}
		objectiveFunctionsList.setModel(model);
	}
	
	/**
	 * Displays configuration panel used by slected mutation.
	 */
	@SuppressWarnings("unchecked")
	private void setMutationPanel() {
		try {
			mutation = ((PrintableType<? extends Mutation>)mutationCombo.getModel().getSelectedItem()).getClassType().newInstance();
		} catch (Exception e) {
			e.printStackTrace();
		}
		mutationConfPanel.removeAll();
		if (mutation.getConfigPanel() != null) {
			mutationConfPanel.add(mutation.getConfigPanel(), BorderLayout.CENTER);
		}
		mutationConfPanel.revalidate();
		mutationConfPanel.repaint();
	}
	
	/**
	 * Displays configuration panel used by slected crossover.
	 */
	@SuppressWarnings("unchecked")
	private void setCrossoverPanel() {
		try {
			crossover = ((PrintableType<? extends Crossover>)crossoverCombo.getModel().getSelectedItem()).getClassType().newInstance();
		} catch (Exception e) {
			e.printStackTrace();
		}
		crossoverConfPanel.removeAll();
		if (crossover.getConfigPanel() != null) {
			crossoverConfPanel.add(crossover.getConfigPanel(), BorderLayout.CENTER);
		}
		crossoverConfPanel.revalidate();
		crossoverConfPanel.repaint();
	}
	
	/**
	 * Displays configuration panel used by slected selection.
	 */
	@SuppressWarnings("unchecked")
	private void setTournamentPanel() {
		try {
			tournament = ((PrintableType<? extends Tournament>)tournamentCombo.getModel().getSelectedItem()).getClassType().newInstance();
		} catch (Exception e) {
			e.printStackTrace();
		}
		tournamentConfPanel.removeAll();
		if (tournament.getConfigPanel() != null) {
			tournamentConfPanel.add(tournament.getConfigPanel(), BorderLayout.CENTER);
		}
		tournamentConfPanel.revalidate();
		tournamentConfPanel.repaint();
	}

	/**
	 * Fills specimen combo box with specimen types accepted by currently chosen algorithm
	 */
	private void setSpecimenCombo() {
		DefaultComboBoxModel model = null;
		if (algorithm != null) {
			model = new DefaultComboBoxModel(algorithm.validateSpecimenTypes(SpecimenTypesHandler.getSpecimenTypes()).toArray());
		} else {
			model = new DefaultComboBoxModel(SpecimenTypesHandler.getSpecimenTypes().toArray());
		}
		specimenTypeCombo.setModel(model);
	}

	/**
	 * Populates combo box with all runs informations, represented by their dates.
	 */
	private void refreshRunsList() {
		File runs = new File(".\\output");
		runs.mkdir();
		File[] runInstances = runs.listFiles(new FileFilter() {
			@Override
			public boolean accept(File file) {
				return (file.isDirectory() && !file.isHidden());
			}
		});
		String[] runNames = new String[runInstances.length];
		for (int i = 0 ; i < runInstances.length ; i++) {
			runNames[i] = runInstances[i].getName();
		}
		DefaultComboBoxModel model = new DefaultComboBoxModel(runNames);
		if (runCombo != null) {
			runCombo.setModel(model);
			refreshRunResultList();
		}
	}
	
	/**
	 * Fills results combo box with available plots for chosen algorithm results.
	 */
	private void refreshRunResultList() {
		if (runCombo.getModel().getSelectedItem() == null) {
			return;
		}
		File runResults = new File(".\\output\\" + runCombo.getSelectedItem().toString());
		File[] runResultInstances = runResults.listFiles(new FileFilter() {
			@Override
			public boolean accept(File file) {
				return file.isDirectory();
			}
		});
		String[] runResultNames = new String[runResultInstances.length];
		for (int i = 0 ; i < runResultInstances.length ; i++) {
			runResultNames[i] = runResultInstances[i].getName();
		}
		DefaultComboBoxModel model = new DefaultComboBoxModel(runResultNames);
		if (resultsCombo != null) {
			resultsCombo.setModel(model);
			refreshStepsList();
		}
		loadOverviewFile();
	}
	
	/**
	 * Reads summary of algorithm run stored in text file, and prints it in prepared text area.
	 */
	private void loadOverviewFile() {
		try {
			if (overviewText == null) {
				return;
			}
			FileInputStream overview = new FileInputStream(".\\output\\" + runCombo.getSelectedItem().toString() + "\\config.txt");
			DataInputStream overviewInput = new DataInputStream(overview);
			BufferedReader overviewReader = new BufferedReader(new InputStreamReader(overviewInput));
			overviewText.setText("");
			String overviewLine;
			try {
				while ((overviewLine = overviewReader.readLine()) != null)
					overviewText.append(overviewLine + "\n");
			} catch (IOException e) {
				overviewText.append("\nERROR: Unable to read rest of file");
				e.printStackTrace();
			}
		} catch (FileNotFoundException e) {
			overviewText.append("ERROR: Overview file not found");
		}
	}
	
	/**
	 * Fills step combo box with list of all steps performed in chosen algorithm run.
	 */
	private void refreshStepsList() {
		File runResults = new File(".\\output\\" + runCombo.getSelectedItem().toString() + "\\" + resultsCombo.getSelectedItem().toString());
		File[] stepInstances = runResults.listFiles(new FileFilter() {
			@Override
			public boolean accept(File file) {
				return file.isDirectory();
			}
		});
		List<String> stepNames = new ArrayList<String>();
		for (int i = 0 ; i < stepInstances.length ; i++) {
			stepNames.add(stepInstances[i].getName());
		}
		Collections.sort(stepNames, new Comparator<String>() {
			@Override
			public int compare(String s1, String s2) {
				return Integer.valueOf(s1).compareTo(Integer.valueOf(s2));
			}
		});
		DefaultComboBoxModel model = new DefaultComboBoxModel(stepNames.toArray());
		if (stepsCombo != null) {
			stepsCombo.setModel(model);
			printStepResults();
		}
	}

	/**
	 * Fills given list instance with data from specified file.
	 * This method is designed to print populations for one step of algorithm.
	 * 
	 * @param source String path to file holding population contents.
	 * @param list	Reference to initialized JList, to print results on.
	 */
	private void fillPopulationList(String source, JList list) {
		File[] populations = (new File(source)).listFiles(new FilenameFilter() {
			@Override
			// Skips graph file
			public boolean accept(File file, String name) {
				return !name.equals("graph.png");
			}
		});
		DefaultListModel model = new DefaultListModel();
		for (int i = 0 ; i < populations.length ; i++) {
			try {
				model.addElement(String.format("Population %d", i));
				BufferedReader populationReader = new BufferedReader(new InputStreamReader(new FileInputStream(new File(String.format("%s\\p%d.txt", source, i)))));
				String line = "";
				while ((line = populationReader.readLine()) != null) {
					model.addElement(line);
				}
			// Uninterrupting exceptions
			} catch (FileNotFoundException e) {
			} catch (IOException e) {
			}
		}
		list.setModel(model);
	}
	
	/**
	 * Fills result list with data from output file for given step in run.
	 */
	private void printStepResults() {
		String path = ".\\output\\" + runCombo.getSelectedItem().toString() + "\\" + resultsCombo.getSelectedItem().toString() + "\\" + stepsCombo.getSelectedItem().toString();
		File genotypePath = new File(path + "\\genotype");
		File img = null;
		if (genotypePath.exists()) {
			fillPopulationList(path + "\\genotype", genotypeList);
			img = new File(path+"\\genotype\\graph.png");
			if (img.exists()) {
				genotypeImage = img.getAbsolutePath();
			}
		}
		File phenotypePath = new File(path + "\\phenotype");
		if (phenotypePath.exists()) {
			fillPopulationList(path + "\\phenotype", phenotypeList);
			img = new File(path+"\\phenotype\\graph.png");
			if (img.exists()) {
				phenotypeImage = img.getAbsolutePath();
			}

		}
	}

	/**
	 * Jemo entry point, everything starts here.
	 * 
	 * @param args parameters passed from command line.
	 */
	public static void main(String[] args) {
		(new MainWindow()).pack();
	}
}
